/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		sdeditor.cpp
 * PURPOSE:		security descriptor editor dialog
 */

namespace SdEditor
{

static const USHORT SDE_LOOKUP_COMPLETED	= WM_USER + 0x100;

HICON DialogIconSm;
HICON DialogIconLg;

struct WindowClassLifetime
{
	WindowClassLifetime()
	{
		DialogIconSm = c_cast<HICON>(LoadImage(SdEditGModule, MAKEINTRESOURCE(IDI_ACLEDITOR), IMAGE_ICON, 16, 16, 0));
		DialogIconLg = c_cast<HICON>(LoadImage(SdEditGModule, MAKEINTRESOURCE(IDI_ACLEDITOR), IMAGE_ICON, 32, 32, 0));
	}
	~WindowClassLifetime()
	{
		DestroyIcon(DialogIconSm);
		DestroyIcon(DialogIconLg);
	}
};

class WindowObject : public DialogWindowBase
{
	SD_EDIT_STATE EditState;
	auto_szpod<WCHAR> ObjName;
	auto_array<auto_szpod<WCHAR> > PathParts;

	Lookup::Translators *Translators;
	Lookup::Enumerators *Enumerators;

	auto_refct<EditEnter::WindowObject> OwnerNameText;
	auto_refct<EditEnter::WindowObject> OwnerSidText;
	auto_refct<EditEnter::WindowObject> GroupNameText;
	auto_refct<EditEnter::WindowObject> GroupSidText;

	auto_ptr<MenuEnum::MenuBase<SID> > SidMenuEnum;

	HMENU SdStateMenu, OwnerStateMenu, GroupStateMenu;

	HWND DaclEditor, SaclEditor, LaclEditor;

	ULONG OwnerUpdateIndex, GroupUpdateIndex;

	SDE_GUI_PREFS Prefs;
	PCWSTR const *Headers;

	DialogCallback Callback;

	ULONG TransparencyKey;

public:
	WindowObject(SDE_GUI_PREFS Prefs, PCWSTR const *Headers, SD_EDIT_STATE const &NewEditState, PCWSTR ObjName, Lookup::Translators *Translators, Lookup::Enumerators *Enumerators, auto_array<auto_szpod<WCHAR> > const &PathParts, HWND Parent, PVOID CallbackContext, DIALOG_CALLBACK_PROC CallbackProc) :
		EditState(NewEditState),
		ObjName(ObjName),
		Translators(Translators),
		Enumerators(Enumerators),
		PathParts(PathParts),
		Prefs(Prefs),
		Headers(Headers),
		DaclEditor(NULL),
		SaclEditor(NULL),
		LaclEditor(NULL),
		SidMenuEnum(auto_take_(Enumerators->SidEnumerator ? new MenuEnum::MenuBase<SID>(Enumerators->SidEnumerator, NULL, Prefs.MenuBreakFactor) : NULL)),
		SdStateMenu(NULL),
		OwnerStateMenu(NULL),
		GroupStateMenu(NULL),
		OwnerUpdateIndex(0),
		GroupUpdateIndex(0),
		Callback(CallbackProc, CallbackContext)
	{
		Create(SdEditGModule, MAKEINTRESOURCE(IDD_SDEDIT), Parent);

		SendMessage(Wnd, WM_SETICON, ICON_SMALL, c_cast<LPARAM>(DialogIconSm));
		SendMessage(Wnd, WM_SETICON, ICON_BIG, c_cast<LPARAM>(DialogIconLg));

		{
			WCHAR Title[MAX_STRING_CHARS];
			swprintf_s(Title, L"SD Edit - %s", ObjName);
			SetWindowText(Wnd, Title);
		}

		if(EditState.Dacl.State & ACL_FLAG_MERGE_CANON_CTRL)
			EditState.Dacl.State |= ACL_MERGE_MODE_INH_LAST; //canon mode only supports two modes, inh last is default
		else if(EditState.Dacl.State & ACL_FLAG_MERGE_FULL_CTRL)
			EditState.Dacl.State |= Prefs.DefaultAclMergeMode << ACL_MERGE_MODE_SHIFT;

		if(EditState.Sacl.State & ACL_FLAG_MERGE_CANON_CTRL)
			EditState.Sacl.State |= ACL_MERGE_MODE_INH_LAST; //canon mode only supports two modes, inh last is default
		else if(EditState.Sacl.State & ACL_FLAG_MERGE_FULL_CTRL)
			EditState.Sacl.State |= Prefs.DefaultAclMergeMode << ACL_MERGE_MODE_SHIFT;

		UpdateDescr(GetDlgItem(Wnd, IDC_DISCR_INFO), NewEditState.Dacl, EditState.State);
		UpdateDescr(GetDlgItem(Wnd, IDC_SYS_INFO), NewEditState.Sacl, EditState.State);
		UpdateDescr(GetDlgItem(Wnd, IDC_LABEL_INFO), NewEditState.Lacl, EditState.State);

		EditState.Owner.Sid = NULL;
		EditState.Group.Sid = NULL;

		SetSidAndStateFields(NewEditState.Owner, TRUE);
		SetSidAndStateFields(NewEditState.Group, FALSE);

		OwnerNameText = new EditEnter::WindowObject(GetDlgItem(Wnd, IDC_OWNER_NAME));
		OwnerSidText = new EditEnter::WindowObject(GetDlgItem(Wnd, IDC_OWNER_SID));
		GroupNameText = new EditEnter::WindowObject(GetDlgItem(Wnd, IDC_GROUP_NAME));
		GroupSidText = new EditEnter::WindowObject(GetDlgItem(Wnd, IDC_GROUP_SID));

		if(!SidMenuEnum)
		{
			EnableWindow(GetDlgItem(Wnd, IDC_ENUM_OWNER), FALSE);
			EnableWindow(GetDlgItem(Wnd, IDC_ENUM_GROUP), FALSE);
		}

		ShowWindow(GetDlgItem(Wnd, IDC_LABEL_INFO), EditState.State & SD_FLAG_LABEL_SUPPORT ? SW_SHOW : SW_HIDE);
		ShowWindow(GetDlgItem(Wnd, IDC_EDIT_LACL), EditState.State & SD_FLAG_LABEL_SUPPORT ? SW_SHOW : SW_HIDE);

		SdStateMenu = CreatePopupMenu();

		AppendMenu(SdStateMenu, MF_STRING | (EditState.State & SD_FLAG_SERVER_SECURITY ? MF_CHECKED : 0), 1, L"Server security");
		AppendMenu(SdStateMenu, MF_STRING | (EditState.State & SD_FLAG_LABEL_SUPPORT ? MF_CHECKED : 0), 9, L"Mandatory label support");
		AppendMenu(SdStateMenu, MF_STRING | (!(EditState.State & SD_FLAG_MERGE_FULL_CTRL) ? MF_GRAYED : 0) | (EditState.State & SD_MERGE_ALL_OBJECTS ? MF_CHECKED : 0), 3, L"Merge all children");
		AppendMenu(SdStateMenu, MF_STRING | MF_GRAYED | (EditState.State & SD_FLAG_CONTAINER ? MF_CHECKED : 0), 0, L"Container");
		AppendMenu(SdStateMenu, MF_STRING | MF_GRAYED | (EditState.State & SD_FLAG_OBJECT_SUPPORT ? MF_CHECKED : 0), 0, L"Object support");
		AppendMenu(SdStateMenu, MF_STRING | MF_GRAYED | (EditState.State & SD_FLAG_MERGE_FULL_CTRL ? MF_CHECKED : 0), 0, L"Full auto-inheritance merge control");

		OwnerStateMenu = CreatePopupMenu();
		GroupStateMenu = CreatePopupMenu();

		SetupSidStatePopupMenu(OwnerStateMenu, EditState.Owner.State);
		SetupSidStatePopupMenu(GroupStateMenu, EditState.Group.State);
	}

private:
	void SetupSidStatePopupMenu(HMENU Menu, ULONG SidState)
	{
		AppendMenu(Menu, MF_STRING | (SidState & SID_FLAG_CHANGED ? MF_CHECKED : 0), 1, L"Changed");
		AppendMenu(Menu, MF_STRING | (SidState & SID_FLAG_PRESENT ? MF_CHECKED : 0), 2, L"Present");
		
		if(SidState & SID_FLAG_READONLY)
			AppendMenu(Menu, MF_STRING | MF_GRAYED | MF_CHECKED, 3, L"Read only");
		
		AppendMenu(Menu, MF_STRING | (SidState & SID_FLAG_DEFAULTED ? MF_CHECKED : 0), 4, L"Defaulted");
		
		if((SidState & SID_MERGE_MODE_BITS) == SID_MERGE_MODE_REPLACE)
			AppendMenu(Menu, MF_STRING | MF_CHECKED, SID_MERGE_MODE_SHIFT + 1, L"Merge replace");
		else if((SidState & SID_MERGE_MODE_BITS) == SID_MERGE_MODE_NONE)
			AppendMenu(Menu, MF_STRING, SID_MERGE_MODE_SHIFT + 1, L"Merge replace");
		else
			AppendMenu(Menu, MF_STRING, SID_MERGE_MODE_SHIFT + 1, L"Unknown merge mode");
	}

	void StateMenuCommand(ULONG &State, HMENU Menu, int CmdId)
	{
		if(CmdId == 0)
			return;

		ULONG Bit = 1 << (CmdId - 1);

		State = (State & ~Bit) | (~State & Bit);

		CheckMenuItem(Menu, CmdId, MF_BYCOMMAND | (State & Bit ? MF_CHECKED : MF_UNCHECKED));

		ShowWindow(GetDlgItem(Wnd, IDC_LABEL_INFO), EditState.State & SD_FLAG_LABEL_SUPPORT ? SW_SHOW : SW_HIDE);
		ShowWindow(GetDlgItem(Wnd, IDC_EDIT_LACL), EditState.State & SD_FLAG_LABEL_SUPPORT ? SW_SHOW : SW_HIDE);
	}

	BOOL ShowButtonMenu(HMENU Menu, HWND ButtonWnd)
	{
		RECT ButtonRect;

		GetWindowRect(ButtonWnd, &ButtonRect);
		
		return TrackPopupMenu(Menu, TPM_RETURNCMD, ButtonRect.left, ButtonRect.bottom, 0, Wnd, NULL);
	}

	void ShowStateMenu(ULONG &State, HMENU Menu, HWND ButtonWnd)
	{
		StateMenuCommand(State, Menu, ShowButtonMenu(Menu, ButtonWnd));
	}

	static void UpdateDescr(HWND InfoBox, ACL_AND_STATE const &AclAndState, ULONG EsState)
	{
		WCHAR Buff[MAX_STRING_CHARS];

		Buff[0] = '\0';

		if(AclAndState.State & ACL_FLAG_PRESENT)
		{
			if(AclAndState.State & ACL_FLAG_LABEL_ACL)
			{
				PCWSTR ChangeState;

				if(AclAndState.State & ACL_FLAG_CHANGED)
					ChangeState = L", changed";
				else if(AclAndState.State & ACL_FLAG_READONLY)
					ChangeState = L", read only";
				else if(EsState & SD_FLAG_LABEL_SET_ANY)
					ChangeState = L", settable to any";
				else
					ChangeState = L"";

				if(AclAndState.Acl)
					switch(CountAcesOfType(AclAndState.Acl, SYSTEM_MANDATORY_LABEL_ACE_TYPE))
					{
					case 0:
						swprintf_s(Buff, L"%sZero mandatory labels%s", Buff, ChangeState);
						break;
					case 1:
						swprintf_s(Buff, L"%sA single mandatory label%s", Buff, ChangeState);
						break;
					default:
						swprintf_s(Buff, L"%sMultiple mandatory labels%s", Buff, ChangeState);
						break;
					}
				else
					swprintf_s(Buff, L"%s\nZero labels (null)", Buff);
			}
			else //non-label ACL
			{
				PCWSTR ChangeState;

				if(AclAndState.State & ACL_FLAG_CHANGED)
					ChangeState = L", changed\n";
				else if(AclAndState.State & ACL_FLAG_READONLY)
					ChangeState = L", read only\n";
				else
					ChangeState = L"";

				if(AclAndState.Acl)
				{
					if(AclAndState.Acl->AceCount == 1)
						swprintf_s(Buff, L"Present, 1 ACE%s", ChangeState);
					else
						swprintf_s(Buff, L"Present, %u ACEs%s", AclAndState.Acl->AceCount, ChangeState);
				}
				else
					swprintf_s(Buff, L"Present, null%s", ChangeState);
			}
		}

		if(AclAndState.State & ACL_FLAG_PRESENT)
			SetWindowText(InfoBox, Buff);
		else
			SetWindowText(InfoBox, L"Unknown/Unspecified");
	}

	struct AclDialogContext
	{
		WindowObject *Wo;
		DialogCallback &Dgc;
		SD_EDIT_STATE const &DialogEs;
		BOOL ForDacl;
		HWND InfoBox;
		HWND *EditorWnd;
		ACL_AND_STATE *AclAndState;

		AclDialogContext(WindowObject *Wo, DialogCallback &Dgc, SD_EDIT_STATE const &Es, BOOL ForDacl, HWND InfoBox, HWND *EditorWnd, ACL_AND_STATE *AclAndState) :
			Wo(Wo),
			Dgc(Dgc),
			DialogEs(Es),
			ForDacl(ForDacl),
			InfoBox(InfoBox),
			EditorWnd(EditorWnd),
			AclAndState(AclAndState)
		{}

		LRESULT Notify(DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
		{
			if(Operation <= DN_CLOSE_MAX)
				SetFocus(GetParent(InfoBox));

			switch(Operation)
			{
			case DN_APPLY:
			case DN_CLOSE_OK:
			{
				ACL_AND_STATE *NewAclAndState = reinterpret_cast<ACL_AND_STATE *>(Data);
				*AclAndState = *NewAclAndState;
				UpdateDescr(InfoBox, *AclAndState, Wo->EditState.State);

				return 0;
			}

			case DN_QUERY_INHERITANCE: //structs are the same in both cases
			case DN_QUERY_DEFAULT:
			{
				DN_QUERY_INHERITANCE_ACL *Acl = reinterpret_cast<DN_QUERY_INHERITANCE_ACL *>(Data);
				DN_QUERY_INHERITANCE_ES Es;
				
				if(ForDacl)
				{
					Es.Es.Dacl.State = Acl->AclAndState.State;
					Es.Es.Dacl.Acl = Acl->AclAndState.Acl.release();
				}
				else
				{
					Es.Es.Sacl.State = Acl->AclAndState.State;
					Es.Es.Sacl.Acl = Acl->AclAndState.Acl.release();
				}

				Es.Es.State = DialogEs.State;
				Es.Es.State |= SD_MERGE_ALL_OBJECTS;

				Dgc.SendCallback(Operation, &Es);

				if(ForDacl)
				{
					Acl->AclAndState.State = Es.Es.Dacl.State;
					Acl->AclAndState.Acl = Es.Es.Dacl.Acl.release();
				}
				else
				{
					Acl->AclAndState.State = Es.Es.Sacl.State;
					Acl->AclAndState.Acl = Es.Es.Sacl.Acl.release();
				}

				return 0;
			}

			case DN_CLOSE_CANCEL:
			default:
				return 0;
			}
		}

		static ULONG_PTR CALLBACK RecieveNotify(PVOID Context, DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
		{
			AclDialogContext *Dgc = static_cast<AclDialogContext *>(Context);

			ULONG_PTR Result = Dgc->Notify(Operation, Data);

			if(Operation <= DN_CLOSE_MAX)
			{
				*Dgc->EditorWnd = NULL;
				delete Dgc;
			}

			return Result;
		}

		AclDialogContext &operator=(AclDialogContext const &x); //no assignment
	};

	void EditAcl(HWND InfoBox, HWND *EditorWnd, ACL_AND_STATE *AclAndState, PCWSTR Descr)
	{
		if(*EditorWnd)
			SetForegroundWindow(*EditorWnd);
		else
		{
			WCHAR Title[MAX_STRING_CHARS];

			swprintf_s(Title, L"SD Edit - %s of %s", Descr, ObjName);

			auto_ptr<AclDialogContext> Dgc = auto_take_(new AclDialogContext(this, Callback, EditState, *EditorWnd == DaclEditor, InfoBox, EditorWnd, AclAndState));
			auto_refct<AclEditor::AclEditor> Wo = new AclEditor::AclEditor(Prefs, Prefs.WindowsIndependent ? HWND_DESKTOP : Wnd, Title, Headers, *AclAndState, EditState.State, Translators, Enumerators, &PathParts, &AclDialogContext::RecieveNotify, Dgc);
			*EditorWnd = Wo->Wnd;
			Dgc.release_unsafe(); //init went ok, the dialog promises to send exactly one close message so Dgc can self-delete
		}
	}

	HWND BoxFor(BOOLEAN Owner, BOOLEAN Sid)
	{
		if(Sid && Owner)
			return GetDlgItem(Wnd, IDC_OWNER_SID);
		if(!Sid && Owner)
			return GetDlgItem(Wnd, IDC_OWNER_NAME);
		if(Sid && !Owner)
			return GetDlgItem(Wnd, IDC_GROUP_SID);
		else //(!Sid && !Owner)
			return GetDlgItem(Wnd, IDC_GROUP_NAME);
	}

	EditEnter::WindowObject *EditEnterFor(BOOLEAN Owner, BOOLEAN Sid)
	{
		if(Sid && Owner)
			return OwnerSidText;
		if(!Sid && Owner)
			return OwnerNameText;
		if(Sid && !Owner)
			return GroupSidText;
		else //(!Sid && !Owner)
			return GroupNameText;
	}

	SID_AND_STATE &SidStateFor(BOOLEAN Owner)
	{
		if(Owner)
			return EditState.Owner;
		else
			return EditState.Group;
	}

	ULONG *UpdateIndexFor(BOOLEAN Owner)
	{
		return Owner ? &OwnerUpdateIndex : &GroupUpdateIndex;
	}

	void ClearAllChangedFlags()
	{
		EditState.Dacl.State &= ~ACL_FLAG_CHANGED;
		UpdateDescr(GetDlgItem(Wnd, IDC_DISCR_INFO), EditState.Dacl, EditState.State);
		
		EditState.Sacl.State &= ~ACL_FLAG_CHANGED;
		UpdateDescr(GetDlgItem(Wnd, IDC_SYS_INFO), EditState.Sacl, EditState.State);

		EditState.Lacl.State &= ~ACL_FLAG_CHANGED;
		UpdateDescr(GetDlgItem(Wnd, IDC_LABEL_INFO), EditState.Lacl, EditState.State);

		EditState.Owner.State &= ~SID_FLAG_CHANGED;
		CheckMenuItem(OwnerStateMenu, 1, MF_BYCOMMAND | MF_UNCHECKED);

		EditState.Group.State &= ~SID_FLAG_CHANGED;
		CheckMenuItem(GroupStateMenu, 1, MF_BYCOMMAND | MF_UNCHECKED);
	}

	struct SidEditCompletionBase
	{
		ULONG UpdateIndex;
		ULONG *SrcUpdateIndex;
		auto_refct<WindowObject> Wo;
		StoredException Se;
		BOOLEAN SidLookup;
		BOOLEAN OwnerLookup;

		SidEditCompletionBase(ULONG *SrcUpdateIndex, WindowObject *Wo, BOOLEAN SidLookup, BOOLEAN OwnerLookup) :
			SrcUpdateIndex(SrcUpdateIndex),
			UpdateIndex(*SrcUpdateIndex),
			Wo(Wo),
			SidLookup(SidLookup),
			OwnerLookup(OwnerLookup)
		{}

		HWND DestWnd()
		{
			return Wo->BoxFor(OwnerLookup, !SidLookup);
		}

		void PostThis()
		{
			if(Wo->Wnd)
				SendMessage(Wo->Wnd, SDE_LOOKUP_COMPLETED, (WPARAM)this, NULL);
		}

		virtual void Finish() = 0;

		virtual ~SidEditCompletionBase()
		{}
	};

	template <class T> struct SidEditCompletionBase2 : SidEditCompletionBase, Lookup::CompletionTarget<T>
	{
		typedef void (WindowObject::*FinishProcType)(SidEditCompletionBase2 *Cb);
		FinishProcType FinishProc;
		auto_szpod<T> Result;

		SidEditCompletionBase2(ULONG *SrcUpdateIndex, FinishProcType FinishProc, WindowObject *Wo, BOOLEAN SidLookup, BOOLEAN OwnerLookup) :
			FinishProc(FinishProc),
			SidEditCompletionBase(SrcUpdateIndex, Wo, SidLookup, OwnerLookup)
		{}

		virtual void Complete(T const *NewResult) override
		{
			Result = NewResult;
			PostThis();
		}

		virtual void CompleteExcept(EXCEPTION_RECORD const *ExceptionRecord) override
		{
			Se = ExceptionRecord;
			PostThis();
		}

		virtual void Finish() override
		{
			(Wo->*FinishProc)(this);
		}
	};

	void SetSidFields(SID const *Sid, BOOLEAN Owner)
	{
		SID_AND_STATE &Dest = SidStateFor(Owner);

		Dest.Sid = Sid;

		SidStateFor(Owner).State |= SID_FLAG_PRESENT | SID_FLAG_CHANGED;

		CheckMenuItem(Owner ? OwnerStateMenu : GroupStateMenu, 0, MF_CHECKED);
		CheckMenuItem(Owner ? OwnerStateMenu : GroupStateMenu, 1, MF_CHECKED);

		auto_szpod<WCHAR> Text(SidToText(Dest.Sid));

		SetWindowText(BoxFor(Owner, TRUE), Text);

		LookupSid(Dest.Sid, Owner);
	}

	void SetSidAndStateFields(SID_AND_STATE const &Src, BOOLEAN Owner)
	{
		SID_AND_STATE &Dest = SidStateFor(Owner);

		Dest.State = Src.State;

		if(Src.State & SID_FLAG_PRESENT)
		{
			SetSidFields(Src.Sid, Owner);
			Dest.State &= ~SID_FLAG_CHANGED;
		}
		else
		{
			CheckMenuItem(Owner ? OwnerStateMenu : GroupStateMenu, 0, MF_UNCHECKED);
			CheckMenuItem(Owner ? OwnerStateMenu : GroupStateMenu, 1, MF_UNCHECKED);

			SetWindowText(BoxFor(Owner, TRUE), L"Unknown/Unspecified");
			SetWindowText(BoxFor(Owner, FALSE), L"Unknown/Unspecified");
		}
	}

	void SidLookupCompleted(SidEditCompletionBase2<WCHAR> *Cb)
	{
		SetWindowText(Cb->DestWnd(), Cb->Result);
	}

	void NameLookupCompleted(SidEditCompletionBase2<SID> *Cb)
	{
		SetSidFields(Cb->Result, Cb->OwnerLookup);
	}

	static void LookupExceptionProc(PCWSTR Title, PCWSTR Text, PVOID Context)
	{
		HWND LEContext = reinterpret_cast<HWND>(Context);

		SetWindowText(LEContext, Text);
	}

	int HandleLookupException(EXCEPTION_POINTERS *ExceptionPointers, BOOLEAN NameLookup, HWND Ctl)
	{
		if(PushLookupErrorText(ExceptionPointers, &LookupExceptionProc, NameLookup, Ctl))
			return EXCEPTION_EXECUTE_HANDLER;
		else
			return EXCEPTION_CONTINUE_SEARCH; //unknown error
	}

	void LookupCompletedGuarded(SidEditCompletionBase *lc)
	{
		if(lc->UpdateIndex == *lc->SrcUpdateIndex)
		{
			lc->Se.ReRaiseIf();
			lc->Finish();
		}
	}

	void LookupCompletedSEH(SidEditCompletionBase *lc)
	{
		__try
		{
			LookupCompletedGuarded(lc);
		}
		__except(HandleLookupException(exception_info(), !lc->SidLookup, lc->DestWnd()))
		{}
	}

	void EnterSidGuarded(BOOLEAN ForOwner)
	{
		ULONG *UpdateIndex = UpdateIndexFor(ForOwner);

		auto_szpod<WCHAR> Text = EditEnterFor(ForOwner, TRUE)->GetEnteredText();
	
		auto_szpod<SID> Sid = TextToSid(Text);

		*UpdateIndex++;

		SetSidFields(Sid, ForOwner);
	}

	void EnterSidSEH(BOOLEAN ForOwner)
	{
		__try
		{
			EnterSidGuarded(ForOwner);
		}
		__except(HandleLookupException(exception_info(), FALSE, BoxFor(ForOwner, FALSE)))
		{}
	}

	void LookupSid(SID const *Sid, BOOLEAN OwnerLookup)
	{
		ULONG *SrcUpdateIndex = UpdateIndexFor(OwnerLookup);

		Translators->SidTranslator->LookupData(new SidEditCompletionBase2<WCHAR>(SrcUpdateIndex, &WindowObject::SidLookupCompleted, this, TRUE, OwnerLookup), Sid);
	}

	void LookupName(PCWSTR Name, BOOLEAN OwnerLookup)
	{
		ULONG *SrcUpdateIndex = UpdateIndexFor(OwnerLookup);

		Translators->SidTranslator->LookupName(new SidEditCompletionBase2<SID>(SrcUpdateIndex, &WindowObject::NameLookupCompleted, this, FALSE, OwnerLookup), Name);
	}

	void ReturnMenuSidTextSEH(MenuEnum::ReturnData<SID> const *Data, BOOLEAN ForOwner)
	{
		__try
		{
			ULONG *UpdateIndex = UpdateIndexFor(ForOwner);

			*UpdateIndex++;
			SetSidFields(Data->Data, ForOwner);
			SetWindowText(BoxFor(ForOwner, FALSE), Data->Name);

			LookupSid(Data->Data, ForOwner);
		}
		__except(HandleLookupException(exception_info(), FALSE, BoxFor(ForOwner, TRUE)))
		{}
	}

	struct MenuPopupContext
	{
		WindowObject *Wo;
		typedef void (WindowObject::*ReturnFuncType)(MenuEnum::ReturnData<SID> const *Data, BOOLEAN ForOwner);
		ReturnFuncType ReturnFunc;
		BOOLEAN ForOwner;

		MenuPopupContext(WindowObject *Wo, ReturnFuncType ReturnFunc, BOOLEAN ForOwner) :
			Wo(Wo),
			ReturnFunc(ReturnFunc),
			ForOwner(ForOwner)
		{}

		void Return(MenuEnum::ReturnData<SID> const *Data, BOOLEAN ForOwner)
		{
			(Wo->*ReturnFunc)(Data, ForOwner);
		}

		static ULONG_PTR CALLBACK RecieveNotify(PVOID Context, DIALOG_NOTIFY_OPERATION Operation, ULONG_PTR Data)
		{
			MenuPopupContext *epc = reinterpret_cast<MenuPopupContext *>(Context);

			if(Operation == DN_CLOSE_OK)
				epc->Return(reinterpret_cast<MenuEnum::ReturnData<SID> *>(Data), epc->ForOwner);

			if(Operation <= DN_CLOSE_MAX) //a close message is always the last one
				delete epc;

			return 0;
		}
	};

	void ShowEnumMenu(auto_ptr<MenuEnum::MenuBase<SID> > &Menu, BOOLEAN ForOwner)
	{
		if(!SidMenuEnum) //no enum supported
			return;

		RECT ButtonRect;

		GetWindowRect(GetDlgItem(Wnd, ForOwner ? IDC_ENUM_OWNER : IDC_ENUM_GROUP), &ButtonRect);

		DialogCallback Dc(&MenuPopupContext::RecieveNotify, new MenuPopupContext(this, &WindowObject::ReturnMenuSidTextSEH, ForOwner));

		Menu->ShowMenu(Wnd, Dc, ButtonRect.left, ButtonRect.bottom);
	}

	virtual LPARAM RecieveMessage(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) override
	{
		switch(msg)
		{
		case WM_COMMAND:
			switch(HIWORD(wparam)) //notification code
			{
			case BN_CLICKED:
				switch(LOWORD(wparam)) //control id
				{
				case IDOK:
					Callback.SendCallback(DN_CLOSE_OK, &EditState);
					ClearAllChangedFlags();
					DestroyWindow(Wnd);
					break;

				case IDC_APPLY:
					Callback.SendCallback(DN_APPLY, &EditState);
					ClearAllChangedFlags();
					break;

				case IDCANCEL:
					Callback.SendCallback(DN_CLOSE_CANCEL, NULL);
					DestroyWindow(Wnd);
					break;

				case IDC_EDIT_DACL:
					EditAcl(GetDlgItem(Wnd, IDC_DISCR_INFO), &DaclEditor, &EditState.Dacl, L"DACL");
					break;

				case IDC_EDIT_SACL:
					EditAcl(GetDlgItem(Wnd, IDC_SYS_INFO), &SaclEditor, &EditState.Sacl, L"SACL");
					break;

				case IDC_EDIT_LACL:
					EditAcl(GetDlgItem(Wnd, IDC_LABEL_INFO), &LaclEditor, &EditState.Lacl, L"Mandatory Label(s)");
					break;

				case IDC_ENUM_GROUP:
				case IDC_ENUM_OWNER:
						ShowEnumMenu(SidMenuEnum, LOWORD(wparam) == IDC_ENUM_OWNER);
					break;

				case IDC_STATE_OWNER:
					ShowStateMenu(EditState.Owner.State, OwnerStateMenu, GetDlgItem(Wnd, IDC_STATE_OWNER));
					break;

				case IDC_STATE_GROUP:
					ShowStateMenu(EditState.Group.State, GroupStateMenu, GetDlgItem(Wnd, IDC_STATE_GROUP));
					break;

				case IDC_STATE_SD:
					ShowStateMenu(EditState.State, SdStateMenu, GetDlgItem(Wnd, IDC_STATE_SD));
					break;
				}
				break;

			case EditEnter::EEN_ENTER:
				switch(LOWORD(wparam)) //control id
				{
				case IDC_OWNER_NAME:
				case IDC_GROUP_NAME:
					{
						BOOLEAN OwnerEntered = LOWORD(wparam) == IDC_OWNER_NAME;
						HWND SidWnd = BoxFor(OwnerEntered, TRUE);

						SetWindowText(SidWnd, L"<Looking up SID>");

						auto_szpod<WCHAR> Text = EditEnterFor(OwnerEntered, FALSE)->GetEnteredText();

						ULONG *UpdateIndex = UpdateIndexFor(OwnerEntered);
						*UpdateIndex++;

						SidStateFor(OwnerEntered).State |= SID_FLAG_CHANGED;

						LookupName(Text, OwnerEntered);
					}
					break;

				case IDC_OWNER_SID:
				case IDC_GROUP_SID:
					EnterSidSEH(LOWORD(wparam) == IDC_OWNER_SID);
					break;
				}
				break;
			}
			break;

		case WM_DESTROY:
			Callback.SendCallback(DN_CLOSE_CANCEL, NULL);
			break;

		case SDE_LOOKUP_COMPLETED:
			{
				SidEditCompletionBase *lc = reinterpret_cast<SidEditCompletionBase *>(wparam);
				LookupCompletedSEH(lc);
			}
			break;

		case WM_INITMENUPOPUP:
			if(SidMenuEnum)
				SidMenuEnum->FillSubMenu(reinterpret_cast<HMENU>(wparam));
			break;
		}

		return DialogWindowBase::RecieveMessage(wnd, msg, wparam, lparam);
	}
};

}//namespace SdEditor
